package 反射;

//反射是为了解决在运行期，对某个实例一无所知的情况下，如何调用其方法。
//反射就是Reflection，Java的反射是指程序在运行期可以拿到一个对象的所有信息。除了int等基本类型外，
//Java的其他类型全部都是class（包括interface）。例如：
//
//String
//Object
//Runnable
//Exception
//...
//仔细思考，我们可以得出结论：class（包括interface）的本质是数据类型（Type）。无继承关系的数据类型无法赋值：
//
//Number n = new Double(123.456); // OK
//String s = new Double(123.456); // compile error!
//而class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时，将其加载进内存。
//每加载一种class，JVM就为其创建一个Class类型的实例，并关联起来。注意：这里的Class类型是一个名叫Class的class。它长这样：
//
//public final class Class {
//  private Class() {}
//}
//以String类为例，当JVM加载String类时，它首先读取String.class文件到内存，然后，为String类创建一个Class实例并关联起来：
//
//Class cls = new Class(String);
//这个Class实例是JVM内部创建的，如果我们查看JDK源码，可以发现Class类的构造方法是private，只有JVM能创建Class实例，
//我们自己的Java程序是无法创建Class实例的。
//
//所以，JVM持有的每个Class实例都指向一个数据类型（class或interface）：
//
//┌───────────────────────────┐
//│      Class Instance       │──────> String
//├───────────────────────────┤
//│name = "java.lang.String"  │
//└───────────────────────────┘
//┌───────────────────────────┐
//│      Class Instance       │──────> Random
//├───────────────────────────┤
//│name = "java.util.Random"  │
//└───────────────────────────┘
//┌───────────────────────────┐
//│      Class Instance       │──────> Runnable
//├───────────────────────────┤
//│name = "java.lang.Runnable"│
//└───────────────────────────┘
//一个Class实例包含了该class的所有完整信息：
//
//┌───────────────────────────┐
//│      Class Instance       │──────> String
//├───────────────────────────┤
//│name = "java.lang.String"  │
//├───────────────────────────┤
//│package = "java.lang"      │
//├───────────────────────────┤
//│super = "java.lang.Object" │
//├───────────────────────────┤
//│interface = CharSequence...│
//├───────────────────────────┤
//│field = value[],hash,...   │
//├───────────────────────────┤
//│method = indexOf()...      │
//└───────────────────────────┘
//由于JVM为每个加载的class创建了对应的Class实例，并在实例中保存了该class的所有信息，包括类名、包名、父类、实现的接口、所有方法、字段等，
//因此，如果获取了某个Class实例，我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
//
//这种通过Class实例获取class信息的方法称为反射（Reflection）。
//
//如何获取一个class的Class实例？有三个方法：
//
//方法一：直接通过一个class的静态变量class获取：
//Class cls = String.class;
//
//方法二：如果我们有一个实例变量，可以通过该实例变量提供的getClass()方法获取：
//String s = "Hello";
//Class cls = s.getClass();
//
//方法三：如果知道一个class的完整类名，可以通过静态方法Class.forName()获取：
//Class cls = Class.forName("java.lang.String");
//
//因为Class实例在JVM中是唯一的，所以，上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例：
//
//Class cls1 = String.class;
//
//String s = "Hello";
//Class cls2 = s.getClass();
//
//boolean sameClass = cls1 == cls2; // true
//注意一下Class实例比较和instanceof的差别：
//
//Integer n = new Integer(123);
//
//boolean b1 = n instanceof Integer; // true，因为n是Integer类型
//boolean b2 = n instanceof Number; // true，因为n是Number类型的子类
//
//boolean b3 = n.getClass() == Integer.class; // true，因为n.getClass()返回Integer.class
//boolean b4 = n.getClass() == Number.class; // false，因为Integer.class!=Number.class
//用instanceof不但匹配指定类型，还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型，但不能作子类型比较。
//
//通常情况下，我们应该用instanceof判断数据类型，因为面向抽象编程的时候，我们不关心具体的子类型。只有在需要精确判断一个类型是不是
//某个class的时候，我们才使用==判断class实例。
//
//因为反射的目的是为了获得某个实例的信息。因此，当我们拿到某个Object实例时，我们可以通过反射获取该Object的class信息：

//void printObjectInfo(Object obj) {
//  Class cls = obj.getClass();
//}
//要从Class实例获取获取的基本信息，参考下面的代码：

/*
public class Class类 {
	public static void main(String[] args) throws Exception {
		int[] n = { 123, 234 };
		System.out.println(n.getClass());
		System.out.println(int.class);
		printClassInfo("".getClass());
		printClassInfo(String.class);// 与上等效
		printClassInfo(Runnable.class);
		printClassInfo(java.time.Month.class);
		printClassInfo(String[].class);
		printClassInfo(int.class);
		
		//获取String的Class实例:
		Class<?> cls = String.class;//?为通配符 或用？改为String 如指定：String,则后面可免强制转型
		//创建一个String实例:
		//String s = (String) cls.newInstance();//弃用，用下面代替
		String s = (String) cls.getDeclaredConstructor().newInstance();//(String)强制转型,如果Class<String> cls 指定：String,则可免强制转型
		s="asdasdasd";
		System.out.println(s);
	}

	static void printClassInfo(Class cls) {
		System.out.println("Class name: " + cls.getName());
		System.out.println("Simple name: " + cls.getSimpleName());
		if (cls.getPackage() != null) {
			System.out.println("Package name: " + cls.getPackage().getName());
		}
		System.out.println("is interface: " + cls.isInterface());
		System.out.println("is enum: " + cls.isEnum());
		System.out.println("is array: " + cls.isArray());
		System.out.println("is primitive: " + cls.isPrimitive());
	}
}
*/
//注意到数组（例如String[]）也是一种Class，而且不同于String.class，它的类名是[Ljava.lang.String。此外，JVM为
//每一种基本类型如int也创建了Class，通过int.class访问。
//如果获取到了一个Class实例，我们就可以通过该Class实例来创建对应类型的实例：
//
////获取String的Class实例:
//Class cls = String.class;
////创建一个String实例:
//String s = (String) cls.newInstance();
//上述代码相当于new String()。通过Class.newInstance()可以创建类实例，它的局限是：只能调用public的无参数构造方法。
//带参数的构造方法，或者非public的构造方法都无法通过Class.newInstance()被调用。

//《动态加载》
//JVM在执行Java程序的时候，并不是一次性把所有用到的class全部加载到内存，而是第一次需要用到class时才加载。例如：

//Main.java
//public class Class类 {
//  public static void main(String[] args) {
//      if (args.length > 0) {
//          create(args[0]);
//      }
//  }
//
//  static void create(String name) {
//      Person p = new Person(name);
//  }
//}
//当执行Main.java时，由于用到了Main，因此，JVM首先会把Main.class加载到内存。然而，并不会加载Person.class，
//除非程序执行到create()方法，JVM发现需要加载Person类时，才会首次加载Person.class。如果没有执行create()方法，
//那么Person.class根本就不会被加载。
//
//这就是JVM动态加载class的特性。
//
//动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性，我们才能在运行期根据条件加载不同的实现类。
//例如，Commons Logging总是优先使用Log4j，只有当Log4j不存在时，才使用JDK的logging。利用JVM动态加载特性，大致的实现代码如下：
//
////Commons Logging优先使用Log4j:
//LogFactory factory = null;
//if (isClassPresent("org.apache.logging.log4j.Logger")) {
//  factory = createLog4j();
//} else {
//  factory = createJdkLog();
//}
//
//boolean isClassPresent(String name) {
//  try {
//      Class.forName(name);
//      return true;
//  } catch (Exception e) {
//      return false;
//  }
//}
//这就是为什么我们只需要把Log4j的jar包放到classpath中，Commons Logging就会自动使用Log4j的原因

//Class类的实例对象，用于记录类描述信息。
//Class类没有公共的构造方法，无法通过new运算符实例化；只能通过对象的getClass方法，或是通过Class.forName(…)来获得实例。
//┌────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐
//│                  方     法                  │                          目           的                            │
//├────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
//│ static ClassforName(String className)      │ 使用参数className来指定具体的类，来获得相关的类描述对象,该方法有可能抛出类加载异常   │
//│    throws ClassNotFoundException           │（ClassNotFoundException）必须捕捉                                     │
//├────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
//│  Class getSuperclass()                     │ 获得当前类描述对象的父类的描述对象。                                        │
//├────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤
//│  String getName()                          │ 返回当前类描述对象的类名称。                                              │
//└────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘

//
//获取Class对象的三种方式：
/*
public class Class类 {
	// Class：类描述对象
	public static void main(String[] args) {
		Class<?> _class;
		//1.对象.getClass()
		String str = "";
		_class = str.getClass();
		System.out.println(_class + "-----对象名.getClass()");
		//2.类.class
		_class = String.class;
		System.out.println(_class + "-----类名.class");
		//3.Class.forName("")
		try {
			_class = Class.forName("java.lang.String");
			System.out.println(_class + "-----Class.forName(...)");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
*/
//《Class类的常用方法》：
//--getName()
//一个Class对象描述了一个特定类的属性，Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体（类、接口、数组类、基本类型或 void）名称。 
//
//--newInstance()
//Class还有一个有用的方法可以为类创建一个实例，这个方法叫做newInstance()。例如：x.getClass.newInstance()，创建了一个同x一样类型的新实例。
//newInstance()方法调用默认构造器（无参数构造器）初始化新建对象。  已被cls.getDeclaredConstructor().newInstance()取代
//
//--getClassLoader()    返回该类的类加载器。
//
//--getComponentType()  返回表示数组组件类型的 Class。
//
//--getSuperclass()     返回表示此 Class 所表示的实体（类、接口、基本类型或 void）的超类的 Class。
//
//--isArray()           判定此 Class 对象是否表示一个数组类。
//--isInterface()       判定此 Class 对象是否表示一个接口。

//我们可以使用Java反射来获取关于类的信息，例如作为其包名称，其访问修饰符等。
//
//--要获得简单的类名，请使用 Class 中的 getSimpleName()方法。
//String simpleName = c.getSimpleName();
//
//--类的修饰符是关键字之前的关键字类在类声明中，如 abstract ， public 。
//Class 中的 getModifiers()方法返回类的所有修饰符。
//getModifiers()方法返回一个整数。我们必须调用 java.lang.reflect.Modifier.toString(int modifiers)以获得修饰符的文本形式。
//
//--要获取超类的名称，请使用 Class 中的 getSuperclass()方法。
//如果对Object类调用getSuperclass()方法，它将返回null，因为它没有超类。
//
//--要获取类实现的所有接口的名称，请使用 getInterfaces()。
//Class[] interfaces = c.getInterfaces();

/*
//通过反射获取类描述，本例为获取MyClass的描述
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;

class MyClass<T> implements Cloneable, Serializable {
	private int id = -1;
	private String name = "Unknown";
	//构造方法
	public MyClass(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public String toString() {
		return "MyClass: id=" + this.id + ", name=" + this.name;
	}
}

public class Class类 {
	public static void main(String[] args) {
		// Print the class declaration for the Class class
		String classDesciption = getClassDescription(MyClass.class);
		System.out.println("classDesciption: "+classDesciption);
	}

	public static String getClassDescription(Class<?> c) {
		StringBuilder classDesc = new StringBuilder();
		System.out.println(Integer.toString(c.getModifiers(),2));//0
		System.out.println(Integer.toString(Modifier.interfaceModifiers(),2));//110000001111
		System.out.println(Integer.toString(Modifier.classModifiers(),2));//110000011111
		int modifierBits = 0;
		String keyword = "";
		if (c.isInterface()) {
			modifierBits = c.getModifiers() & Modifier.interfaceModifiers();//3087 [0xc0f
			if (c.isAnnotation()) {
				keyword = "@interface";
			} else {
				keyword = "interface";
			}
		} else if (c.isEnum()) {
			modifierBits = c.getModifiers() & Modifier.classModifiers();
			keyword = "enum";
		}
		modifierBits = c.getModifiers() & Modifier.classModifiers();//判断类的修饰符
		keyword = "class";

		String modifiers = Modifier.toString(modifierBits);//获取类的名称字符
		classDesc.append(modifiers);
		classDesc.append(" " + keyword);
		String simpleName = c.getSimpleName();//MyClass
		classDesc.append(" " + simpleName);

		String genericParms = getGenericTypeParams(c);//<T>
		classDesc.append(genericParms);

		Class<?> superClass = c.getSuperclass();
		if (superClass != null) {
			String superClassSimpleName = superClass.getSimpleName();
			classDesc.append("  extends " + superClassSimpleName);
		}
		String interfaces = Class类.getClassInterfaces(c);
		if (interfaces != null) {
			classDesc.append("  implements " + interfaces);
		}
		return classDesc.toString();
	}

	public static String getClassInterfaces(Class c) {
		Class<?>[] interfaces = c.getInterfaces();
		String interfacesList = null;
		if (interfaces.length > 0) {
			String[] interfaceNames = new String[interfaces.length];
			for (int i = 0; i < interfaces.length; i++) {
				interfaceNames[i] = interfaces[i].getSimpleName();
			}
			interfacesList = String.join(", ", interfaceNames);
		}
		return interfacesList;
	}

	public static String getGenericTypeParams(Class c) {
		StringBuilder sb = new StringBuilder();
		TypeVariable<?>[] typeParms = c.getTypeParameters();

		if (typeParms.length > 0) {
			String[] paramNames = new String[typeParms.length];
			for (int i = 0; i < typeParms.length; i++) {
				paramNames[i] = typeParms[i].getTypeName();
			}
			sb.append("<");
			String parmsList = String.join(",", paramNames);
			sb.append(parmsList);
			sb.append(">");
		}
		return sb.toString();
	}
}*/
//上面的代码生成以下结果。
//
//class MyClass<T>  extends Object  implements Cloneable, Serializable

//Java Class类
//Java中，无论生成某个类的多少个对象，这些对象都会对应于同一个Class对象，这个Class对象是由JVM生成的，通过它能够获悉整个类的结构。要想使用反射，
//首先需要获得待操作的类所对应的Class对象。
//
//原理
//所有的java类都是继承了object这个类，在object这个类中有一个方法：getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用，这个引
//用指向的是Class类的对象。我们自己无法生成一个Class对象（构造函数为private)，而这个Class类的对象是在当各类被调入时，由 Java 虚拟机自动创
//建 Class 对象，或通过类装载器中的 defineClass 方法生成。我们生成的对象都会有个字段记录该对象所属类在CLass类的对象的所在位置。如下图所示：
//
//获取class对象方法
//1.使用Class类的静态方法
//eg: Class c=Class.forName(“java.lang.String”);
//
//2.使用类的.class语法
//eg：Class c = Employee.class;
//
//3.使用对象的getClass()方法
//eg：Employee e = new Employee();  Class c3 = e.getClass(); 
//
//例子

//public class ClassTest {
//  public static void main(String [] args)throws Exception{
//      String str1="abc";
//      Class cls1=str1.getClass();
//      Class cls2=String.class;
//      Class cls3=Class.forName("java.lang.String");
//      System.out.println(cls1==cls2);
//      System.out.println(cls1==cls3);
//  }
//}
//返回结果为：true,true.
//
//方法
//forName(String classname)
//该方法返回给定串名相应的Class对象。
//
//getClassLoader()
//获取该类的类装载器。
//
//getComponentType()
//如果当前类表示一个数组，则返回表示该数组组件的Class对象，否则返回null。
//
//getConstructor(Class[])
//返回当前Class对象表示的类的指定的公有构造子对象。
//
//getConstructors()
//返回当前Class对象表示的类的所有公有构造子对象数组。
//
//getDeclaredConstructor(Class[])
//返回当前Class对象表示的类的指定已说明的一个构造子对象。
//
//getDeclaredConstructors()
//返回当前Class对象表示的类的所有已说明的构造子对象数组。
//
//getDeclaredField(String)
//返回当前Class对象表示的类或接口的指定已说明的一个域对象。
//
//getDeclaredFields()
//返回当前Class对象表示的类或接口的所有已说明的域对象数组。
//
//getDeclaredMethod(String,Class[])
//返回当前Class对象表示的类或接口的指定已说明的一个方法对象。
//
//getDeclaredMethods()
//返回Class对象表示的类或接口的所有已说明的方法数组。
//
//getField(String)
//返回当前Class对象表示的类或接口的指定的公有成员域对象。
//
//getFields()
//返回当前Class对象表示的类或接口的所有可访问的公有域对象数组。
//
//getInterfaces()
//返回当前对象表示的类或接口实现的接口。
//
//getMethod(String,Class[])
//返回当前Class对象表示的类或接口的指定的公有成员方法对象。String:方法名字符串，Class[]:参数的类名数组
//
//getMethods()
//返回当前Class对象表示的类或接口的所有公有成员方法对象数组，包括已声明的和从父类继承的方法。
//
//getModifiers()
//返回该类或接口的Java语言修改器代码。
//
//getName()
//返回Class对象表示的类型(类、接口、数组或基类型)的完整路径名字符串。
//
//getResource(String)
//按指定名查找资源。
//
//getResourceAsStream(String)
//用给定名查找资源。
//
//getSigners()
//获取类标记。
//
//getSuperclass()
//如果此对象表示除Object外的任一类,那么返回此对象的父类对象。
//
//isArray()
//如果Class对象表示一个数组则返回true,否则返回false。
//
//isAssignableFrom(Class)
//判定Class对象表示的类或接口是否同参数指定的Class表示的类或接口相同，或是其父类。
//
//isInstance(Object)
//此方法是Java语言instanceof操作的动态等价方法。
//
//isInterface()
//判定指定的Class对象是否表示一个接口类型。
//
//isPrimitive()
//判定指定的Class对象是否表示一个Java的基类型。
//
//newInstance()
//创建类的新实例。
//
//toString()
//将对象转换为字符串。
//
//例子

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class Personc {
	public Personc() {
	}

	public Personc(String name) {
		this.name = name;
	}

	public Personc(int age) {
		this.age = age;
	}

	public Personc(String name, int age) {
		this.age = age;
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	@Override
	public String toString() {
		return "[" + this.name + "  " + this.age + "]";
	}

	private String name;
	private int age;
}

public class Class类 {
	public static void main(String[] args) {
		Class<?> demo = null;
//      try {
//         demo = Class.forName("反射.Person");
//      } catch(Exception e) {
//          e.printStackTrace();
//      }
//      Class<?> demo=Person.class;
		try {
			// demo=Class.forName("反射.Personc");//显式加载
			demo = ClassLoader.getSystemClassLoader().loadClass("反射.Personc");// 显式加载
			System.out.println(demo.getClassLoader().getClass().getName());// jdk.internal.loader.ClassLoaders$AppClassLoader
			// Method defineClass = ClassLoader.class.getDeclaredMethod;

			// Class<?> c = ClassLoader.findLoadedClass(反射.Person.class);
			// 【系统类加载器】
			// ▪java语言编写，由sun.misc.Launcher$ClassLoader实现
			// ▪继承于ClassLoader类
			// ▪父类加载器为扩展类加载器,jdk9为PlatformClassLoader
			// ▪他负责加载环境变量classpath或系统属性 java.class.path 指定路径下的类库
			// ▪应用程序中的类加载器默认是系统类加载器。
			// ▪它是用户自定义类加载器的默认父加载器
			// ▪通过ClassLoader的getSystemClassLoader（）方法可以获取到该类加载器
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		Personc per1 = null;
		Personc per2 = null;
		Personc per3 = null;
		Personc per4 = null;
		// 取得全部的构造函数
		Constructor<?> cons[] = demo.getConstructors();
		//System.out.println(cons[1]);
		for (int i = 0; i < 4; i++) {
			System.out.println(cons[i]);
		}
		try {
			per1 = (Personc) cons[3].newInstance();
			per2 = (Personc) cons[2].newInstance("Rollen");
			per3 = (Personc) cons[1].newInstance(20);
			per4 = (Personc) cons[0].newInstance("Rollen", 20);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(per1);
		System.out.println(per2);
		System.out.println(per3);
		System.out.println(per4);
	}
}


//执行结果：
//
//[null  0]
//[Rollen  0]
//[null  20]
//[Rollen  20]

//总结
//1. 在运行期间，如果我们要产生某个类的对象，Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载，JVM会根据类的
//名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存，就可以用它来产生该类型的所有对象，虚拟机只会产生一份字节码， 
//用这份字节码可以产生多个实例对象。
//
//2. 一个Java类中用一个Class类的对象来表示，一个类中的组成部分：成员变量，方法，构造方法，包等等信息也用一个个的Java类来表示，就
//像汽车是一个类，汽车中的发动机，变速箱等等也是一个个的类。表示Java类的Class类显示要提供一系列的方法，来获得其中的变量，方法，构造
//方法，修饰符，包等信息，这些信息就是用相应类的实例对象来表示，它们是Field、Method、Contructor、Package等等。



